﻿Imports System.Collections.ObjectModel
Imports System.Windows.Controls.Primitives
Imports WPFSearchDataGrid.EventAggregator
Imports WPFSearchDataGrid.Assets

Public Class WPFSearchDataGrid
    Inherits UserControl
    Implements IListener(Of DataItemValueChanged)

#Region "   Attributes"

    Private m_scrollchangedevent As ScrollChangedEventHandler
    Private m_records As ObservableCollection(Of Record) = New ObservableCollection(Of Record)()
    Dim m_publisher As New EventPublisher()
    Private m_datagrid As DataGrid = Nothing

#End Region

#Region "   Properties"

#Region "   Records"

    Public Property Records As ObservableCollection(Of Record)
        Get
            Return m_records
        End Get
        Set(value As ObservableCollection(Of Record))
            m_records = value
        End Set
    End Property

#End Region

#Region "   ModuleName"

    Private ReadOnly Property ModuleName As String
        Get
            Return "WWSearchDataGrid"
        End Get
    End Property

#End Region

#End Region

#Region "   Helper Functions"

    Private Function FindVisualChild(Of T As DependencyObject)(obj As DependencyObject) As T
        For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
            Dim child As DependencyObject = VisualTreeHelper.GetChild(obj, i)
            If child IsNot Nothing AndAlso TypeOf child Is T Then
                Return DirectCast(child, T)
            Else
                Dim childOfChild As T = FindVisualChild(Of T)(child)
                If childOfChild IsNot Nothing Then
                    Return childOfChild
                End If
            End If
        Next
        Return Nothing
    End Function

    Public Function FindAncestorOrSelf(Of T As DependencyObject)(obj As DependencyObject) As T
        While obj IsNot Nothing
            Dim objtest As T = TryCast(obj, T)
            If objtest IsNot Nothing Then
                Return objtest
            End If
            obj = GetParent(obj)
        End While
        Return Nothing
    End Function

    Public Function GetParent(obj As DependencyObject) As DependencyObject
        If IsNothing(obj) Then
            Return Nothing
        End If
        Dim _ce As ContentElement = TryCast(obj, ContentElement)
        If Not IsNothing(_ce) Then
            Dim _parent As DependencyObject = ContentOperations.GetParent(_ce)
            If Not IsNothing(_parent) Then
                Return Parent
            End If
            Dim _fce As FrameworkContentElement = TryCast(_ce, FrameworkContentElement)
            Return If(Not IsNothing(_fce), _fce.Parent, Nothing)
        End If
        Return VisualTreeHelper.GetParent(obj)
    End Function

#End Region

#Region "   Dependency Properties"

#Region "   ParentScrollBarVisibility"

    Public Property ParentScrollBarVisibility As Visibility
        Get
            Return GetValue(ParentScrollBarVisibilityProperty)
        End Get

        Set(ByVal value As Visibility)
            SetValue(ParentScrollBarVisibilityProperty, value)
        End Set
    End Property

    Public Shared ReadOnly ParentScrollBarVisibilityProperty As DependencyProperty = _
                           DependencyProperty.Register("ParentScrollBarVisibility", _
                           GetType(Visibility), GetType(WPFSearchDataGrid), _
                           New PropertyMetadata(Visibility.Hidden))


#End Region

#Region "   ShowHeader"

    Public Property ShowHeader As Visibility
        Get
            Return GetValue(ShowHeaderProperty)
        End Get

        Set(ByVal value As Visibility)
            SetValue(ShowHeaderProperty, value)
        End Set
    End Property

    Public Shared ReadOnly ShowHeaderProperty As DependencyProperty = _
                           DependencyProperty.Register("ShowHeader", _
                           GetType(Visibility), GetType(WPFSearchDataGrid), _
                           New PropertyMetadata(Visibility.Visible, AddressOf OnHeaderChanged))

    Private Shared Sub OnHeaderChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim c As WPFSearchDataGrid = d
        If Not IsNothing(c) Then
            Dim btvc As New BooleanToVisibilityConverter
            If btvc.ConvertBack(e.NewValue, GetType(Boolean), Nothing, Nothing) Then
                c.rowtoHide.Height = New GridLength(22)
            Else
                c.rowtoHide.Height = New GridLength(0)
            End If
        End If
    End Sub

#End Region

#Region "   ShowRefresh"

    Public Property ShowRefresh As Visibility
        Get
            Return GetValue(ShowRefreshProperty)
        End Get

        Set(ByVal value As Visibility)
            SetValue(ShowRefreshProperty, value)
        End Set
    End Property


    Public Shared ReadOnly ShowRefreshProperty As DependencyProperty = _
                               DependencyProperty.Register("ShowRefresh", _
                               GetType(Visibility), GetType(WPFSearchDataGrid), _
                               New PropertyMetadata(Visibility.Visible, AddressOf OnRefreshChanged))


    Private Shared Sub OnRefreshChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim c As WPFSearchDataGrid = d
        If Not IsNothing(c) Then
            c.btnRefresh.Visibility = e.NewValue
        End If
    End Sub

#End Region

#Region "   ShowAdvanced"

    Public Shared ReadOnly ShowAdvancedProperty As DependencyProperty = _
                               DependencyProperty.Register("ShowAdvanced", _
                               GetType(Visibility), GetType(WPFSearchDataGrid), _
                               New PropertyMetadata(Visibility.Visible, AddressOf OnAdvancedChanged))

    Private Shared Sub OnAdvancedChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim c As WPFSearchDataGrid = d
        If Not IsNothing(c) Then

        End If
    End Sub

    Public Property ShowAdvanced As Visibility
        Get
            Return GetValue(ShowAdvancedProperty)
        End Get

        Set(ByVal value As Visibility)
            SetValue(ShowAdvancedProperty, value)
        End Set
    End Property

#End Region

#End Region

#Region "   Routed Events"

    Public Custom Event FilterChanged As RoutedEventHandler

        AddHandler(ByVal value As RoutedEventHandler)
            Me.AddHandler(FilterChangedEvent, value)
        End AddHandler

        RemoveHandler(ByVal value As RoutedEventHandler)
            Me.RemoveHandler(FilterChangedEvent, value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Me.RaiseEvent(e)
        End RaiseEvent
    End Event

    Public Shared ReadOnly FilterChangedEvent As RoutedEvent = _
                      EventManager.RegisterRoutedEvent("FilterChanged", _
                      RoutingStrategy.Bubble, _
                      GetType(RoutedEventHandler), GetType(WPFSearchDataGrid))

#End Region

#Region "   Methods"

#Region "   Clear"

    Private Sub Clear()

        Try
            Dim _record As Record = m_records.First()
            Dim key As String = Nothing
            For i As Integer = 0 To _record.Properties.Count - 1
                key = _record.Properties().ElementAt(i).Key
                _record.Properties(key).Reset(False)
            Next
            dgSearch.Items.Refresh()
            _record.Properties(key).Reset(True)
        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
                                       , String.Format("{0} Error...", ModuleName) _
                                       , MessageBoxButton.OK _
                                       , MessageBoxImage.Error)
        End Try

    End Sub

#End Region

#Region "   Constructors"

    Public Sub New()
        InitializeComponent()
        m_publisher.AddListener(Me)
    End Sub

#End Region

#End Region

#Region "   EventHandlers"

#Region "   cmnuSearch_Reset_Click"

    Private Sub cmnuSearch_Reset_Click(sender As System.Object, e As RoutedEventArgs)

        Try
            Clear()
        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
                                       , String.Format("{0} Error...", ModuleName) _
                                       , MessageBoxButton.OK _
                                       , MessageBoxImage.Error)
        End Try

    End Sub

#End Region

#Region "   cmnuSearch_Refresh_Click"

    Private Sub cmnuSearch_Refresh_Click(sender As System.Object, e As RoutedEventArgs)

        Try
            'Clear()
        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
                                       , String.Format("{0} Error...", ModuleName) _
                                       , MessageBoxButton.OK _
                                       , MessageBoxImage.Error)
        End Try

    End Sub

#End Region

#Region "   cmnuSearch_Advanced_Click"

    Private Sub cmnuSearch_Advanced_Click(sender As System.Object, e As RoutedEventArgs)

        Try

        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
                                       , String.Format("{0} Error...", ModuleName) _
                                       , MessageBoxButton.OK _
                                       , MessageBoxImage.Error)
        End Try

    End Sub

#End Region

#Region "   btnSearch_Click"

    Private Sub btnSearch_Click(sender As System.Object, e As RoutedEventArgs) Handles btnSearch.Click

        Try
            TryCast(sender, Button).ContextMenu.IsEnabled = True
            TryCast(sender, Button).ContextMenu.PlacementTarget = TryCast(sender, Button)
            TryCast(sender, Button).ContextMenu.Placement = PlacementMode.Bottom
            TryCast(sender, Button).ContextMenu.IsOpen = True

        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
                                       , String.Format("{0} Error...", ModuleName) _
                                       , MessageBoxButton.OK _
                                       , MessageBoxImage.Error)
        End Try

    End Sub

#End Region

#Region "   dgSearch_ScrollChanged"

    Private Sub dgSearch_ScrollChanged(sender As Object, e As ScrollChangedEventArgs)

        Try
            svSearch.ScrollToHorizontalOffset(e.HorizontalOffset)
        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
                                       , String.Format("{0} Error...", ModuleName) _
                                       , MessageBoxButton.OK _
                                       , MessageBoxImage.Error)
        End Try

    End Sub

#End Region

#Region "   WWSearchDataGrid_DataContextChanged"

    Private Sub WWSearchDataGrid_DataContextChanged(sender As Object, e As DependencyPropertyChangedEventArgs) Handles Me.DataContextChanged
        Try
            If TypeOf e.NewValue Is DataGrid Then
                m_datagrid = e.NewValue
                Records.Clear()
                dgSearch.Columns.Clear()
                Dim dgcolumn As CustomBoundColumn
                dgSearch.RowHeaderWidth = m_datagrid.RowHeaderWidth
                m_records.Add(New Record)
                AddHandler m_datagrid.Loaded, AddressOf m_datagrid_loaded
                For Each _datagridcolumn As Object In m_datagrid.Columns()
                    Dim _dataitem As IDataItem = DataItemFactory.Resolve(_datagridcolumn, m_publisher)
                    m_records.Item(0).Properties.Add(_datagridcolumn.Header, _dataitem)
                    dgcolumn = New CustomBoundColumn() With {.Header = _datagridcolumn.Header.ToString()}
                    dgcolumn.TemplateName = _dataitem.TemplateName
                    'bind the any column with an itemssource to our customboundcolumn
                    If _dataitem.HasItemsSource Then
                        Dim its As Binding = New Binding("ItemsSource") With {.Source = _datagridcolumn}
                        BindingOperations.SetBinding(dgcolumn, DataGridComboBoxColumn.ItemsSourceProperty, its)
                    End If
                    'bind the source columns actualwidth property to our new datacolumns width property.
                    Dim bnd As Binding = New Binding("ActualWidth") With {.Source = _datagridcolumn}
                    BindingOperations.SetBinding(dgcolumn, DataGridColumn.WidthProperty, bnd)
                    dgSearch.Columns.Add(dgcolumn)
                Next
                If Not IsNothing(m_scrollchangedevent) Then
                    m_datagrid.RemoveHandler(ScrollViewer.ScrollChangedEvent, m_scrollchangedevent)
                End If
                m_scrollchangedevent = New ScrollChangedEventHandler(AddressOf dgSearch_ScrollChanged)
                m_datagrid.AddHandler(ScrollViewer.ScrollChangedEvent, m_scrollchangedevent, True)
                dgSearch.RowHeight = 20
                dgSearch.ItemsSource = Records
            End If
        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
                           , String.Format("{0} Error...", ModuleName) _
                           , MessageBoxButton.OK, MessageBoxImage.Error)
        End Try

    End Sub

#End Region

#Region "   m_datagrid_loaded"

    Private Sub m_datagrid_loaded(sender As Object, e As RoutedEventArgs)
        Try
            Dim ssv As ScrollViewer = FindVisualChild(Of ScrollViewer)(m_datagrid)
            Dim a As Binding = New Binding("ComputedVerticalScrollBarVisibility") With {.Source = ssv}
            BindingOperations.SetBinding(Me, ParentScrollBarVisibilityProperty, a)
        Catch ex As Exception
        End Try
    End Sub

#End Region

#Region "   dgSearch_PreviewKeyDown"

    Private Sub dgSearch_PreviewKeyDown(sender As System.Object, e As KeyEventArgs) Handles dgSearch.PreviewKeyDown
        Try
            Dim frameworkElement = TryCast(Keyboard.FocusedElement, FrameworkElement)
            If (frameworkElement IsNot Nothing) Then
                If e.Key = Key.Enter Then
                    e.Handled = True
                    frameworkElement.MoveFocus(New TraversalRequest(FocusNavigationDirection.Next))
                Else
                    dgSearch.BeginEdit()
                    Dim z = FindVisualChild(Of TextBox)(Keyboard.FocusedElement)
                    If z IsNot Nothing Then
                        Keyboard.Focus(z)
                        z.SelectAll()
                    End If
                End If
            End If
        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
               , String.Format("{0} Error...", ModuleName) _
               , MessageBoxButton.OK _
               , MessageBoxImage.Error)
        End Try
    End Sub

#End Region

#Region "   IListener Implementation"

    Public Sub Handle(subject As DataItemValueChanged) Implements IListener(Of DataItemValueChanged).Handle

        Try
            Dim args As New RoutedEventArgs(FilterChangedEvent, m_records.First())
            RaiseEvent FilterChanged(Me, args)
        Catch ex As Exception
            MessageBox.Show(String.Format("Error during {0}.{1}{2}", Reflection.MethodBase.GetCurrentMethod().Name, vbCrLf, ex.Message) _
               , String.Format("{0} Error...", ModuleName) _
               , MessageBoxButton.OK _
               , MessageBoxImage.Error)
        End Try

    End Sub

#End Region

#End Region

End Class
